Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Exceptions
8 Die Funktionsbibliothek
9 Threads und nebenläufige Programmierung
10 Raum und Zeit
11 Datenstrukturen und Algorithmen
12 Dateien und Datenströme
13 Die eXtensible Markup Language (XML)
14 Grafische Oberflächen mit Swing
15 Grafikprogrammierung
16 Das Netz
17 JavaServer Pages und Servlets
18 Verteilte Programmierung mit RMI und Web–Services
19 Applets, Midlets und Sound
20 Datenbankmanagement mit JDBC
21 Reflection und Annotationen
22 Komponenten durch Bohnen
23 Logging und Monitoring
24 Sicherheitskonzepte
25 Java Native Interface (JNI)
26 Dienstprogramme für die Java-Umgebung
A Die Begleit-DVD
Index

Download:
- ZIP, ca. 12,5 MB
Buch bestellen

Website zum Buch
Weblog des Autors
Ihre Meinung?

Spacer
 <<   zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren mit der Java Standard Edition Version 6
Buch: Java ist auch eine Insel

Java ist auch eine Insel
6., akt. und erw. Aufl., mit DVD
1.454 S., 49,90 Euro
Galileo Computing
ISBN 3-89842-838-9
gp 18 Verteilte Programmierung mit RMI und Web–Services
  gp 18.1 Entfernte Objekte und Methoden
    gp 18.1.1 Stellvertreter helfen bei entfernten Methodenaufrufen
    gp 18.1.2 Standards für entfernte Objekte
  gp 18.2 Java Remote Method Invocation
    gp 18.2.1 Zusammenspiel von Server, Registry und Client
    gp 18.2.2 Wie die Stellvertreter die Daten übertragen
    gp 18.2.3 Probleme mit entfernten Methoden
    gp 18.2.4 Nutzen von RMI bei Middleware-Lösungen
    gp 18.2.5 Zentrale Klassen und Schnittstellen
    gp 18.2.6 Entfernte und lokale Objekte im Vergleich
  gp 18.3 Auf der Serverseite
    gp 18.3.1 Entfernte Schnittstelle deklarieren
    gp 18.3.2 Remote-Objekt-Implementierung
    gp 18.3.3 Stellvertreterobjekte erzeugen
    gp 18.3.4 Der Namensdienst (Registry)
    gp 18.3.5 Remote-Objekt-Implementierung exportieren und beim Namensdienst anmelden
    gp 18.3.6 Einfaches Logging
    gp 18.3.7 Aufräumen mit dem DGC
  gp 18.4 Auf der Clientseite
  gp 18.5 Entfernte Objekte übergeben und laden
    gp 18.5.1 Klassen vom RMI-Klassenlader nachladen
  gp 18.6 Der Server startet die Registry selbst
  gp 18.7 Weitere Eigenschaften von RMI
    gp 18.7.1 RMI und CORBA
    gp 18.7.2 RMI über HTTP getunnelt
    gp 18.7.3 Automatische Remote-Objekt-Aktivierung
  gp 18.8 Daily Soap
    gp 18.8.1 SOAP-Protokoll
    gp 18.8.2 Die technische Realisierung
    gp 18.8.3 SOAP-Implementierungen
    gp 18.8.4 @WebService in Java 6
    gp 18.8.5 Einen Web-Service definieren
    gp 18.8.6 Web-Services veröffentlichen
    gp 18.8.7 Einen JAX-WS-Client implementieren
  gp 18.9 Java Message Service (JMS)
  gp 18.10 Zum Weiterlesen


Galileo Computing

18.3 Auf der Serverseite  downtop

Um entfernte Objekte mit ihren Methoden in Java-Programmen zu nutzen, sind einige Arbeitsschritte nötig, die wir im Folgenden kurz skizzieren. An den Schritten spiegelt sich der Programmieraufwand wider:

1.  Eine entfernte Schnittstelle deklariert Methoden.
2. Eine Klasse implementiert die Schnittstelle und füllt die Methode mit Leben. Dies bildet die Remote-Objekt-Implementierung.
       
3. Mit dieser Implementierung benötigen wir ein exportiertes Exemplar. Wir melden es beim Namensdienst an, damit der Client es finden kann. Dies schließt die Serverseite ab.
       

Galileo Computing

18.3.1 Entfernte Schnittstelle deklarieren  downtop

Damit der Client eine entfernte Methode nutzen kann, muss er ein Stellvertreterobjekt befragen. Dieses packt die Daten ein und übermittelt sie. Die Stellvertreterobjekte erzeugt Java selbständig. Damit der Generator korrekten Quellcode für die Übertragung erstellen kann, ist eine Beschreibung nötig. Die Deklaration muss die Signatur eindeutig spezifizieren, und damit weiß der Client, wie die Funktion aussieht, die er aufrufen kann, und der Server kann die Methode dann beschreiben. Normalerweise gibt es für die Spezifikation der entfernten Funktionen spezielle Beschreibungssprachen (wie IDL bei CORBA), doch bei RMI reicht es, ein Java-Interface mit den Methoden zu deklarieren.

Listing 18.1    com/javatutor/insel/rmi/Adder.java

package com.javatutor.insel.rmi; 
 
import java.rmi.Remote; 
import java.rmi.RemoteException; 
 
public interface Adder extends Remote 
{ 
  int add( int x, int y ) throws RemoteException; 
}

An diesem Beispiel können wir mehrere wichtige Eigenschaften der Schnittstelle ablesen:

  • Die entfernte Schnittstelle ist öffentlich. Wenn sie nur paketsichtbar oder eingeschränkter ist, kann der Client die entfernte Methode nicht finden, wenn er danach verlangt.
  • Die eigene Schnittstelle erweitert die Schnittstelle Remote. Nur die Klassen, die Remote implementieren, können entfernte Methoden anbieten. Remote ist allerdings leer und damit eine Markierungsschnittstelle.
  • Die angebotenen Methoden können unbeabsichtigte Fehler auslösen, zum Beispiel, wenn das Transportsystem zusammenbricht. Für diesen Fall muss jede Methode RemoteException in einer throws-Anweisung aufführen.
  • Eine entfernte Funktion darf Parameter besitzen. Sind die Argumente primitive Werte, werden diese einfach übertragen. Handelt es sich um Objekte, so müssen diese serialisierbar sein.

Galileo Computing

18.3.2 Remote-Objekt-Implementierung  downtop

Der Client nutzt letztendlich das vom Server bereitgestellte entfernte Objekt. Der Server steht in der Pflicht, eine Implementierung der Remote-Schnittstelle anzugeben, so dass diese im nächsten Schritt exportiert und damit angemeldet werden kann.

Die Implementierung der Geschäftslogik ist einfach:

Listing 18.2    com/javatutor/insel/rmi/AdderImpl.java

package com.javatutor.insel.rmi; 
 
public class AdderImpl implements Adder 
{ 
  public int add( int x, int y ) 
  { 
    return x + y; 
  } 
}

Da die Klasse eine Implementierung der Schnittstelle ist, geben wir ihr die Endung Impl. (Das ist eine bekannte Namensgebung, aber keine Pflicht.)

Es steht frei, andere Methoden anzugeben, die nicht in der Schnittstelle vorgegeben sind, doch sind diese natürlich nicht nach außen hin sichtbar. Die Argumente und Rückgabewerte können von jedem beliebigen Datentyp sein. Bei primitiven Datentypen werden spezielle read()- und write()-Folgen generiert. Objekte müssen die Schnittstelle Serializable implementieren (oder Remote sein). Dann werden die lokalen Objekte als Kopie übertragen. Über die Serialisierung werden alle nicht statischen und nicht transienten Attribute übermittelt. Ist das Argument wiederum instanceof Remote, wird dieser Verweis als einfache Referenz übergeben. In Wirklichkeit ist die Referenz ein Verweis auf den Stellvertreter.


Galileo Computing

18.3.3 Stellvertreterobjekte erzeugen  downtop

Die Stellvertreter sind Methoden auf der Client- und Serverseite, die die tatsächliche Kommunikation betreiben. Sie müssen für jede Methode oder jede Parameteränderung neu generiert werden. Von Hand müssen diese Stellvertreter nicht programmiert werden; das übernimmt Java. Vor Java 5 musste der RMI-Compiler rmic verwendet werden, der aus einer Methodenbeschreibung die Stellvertreter generiert. Das ist mit Java 5 automatisiert. So werden der Stub (der Stellvertreter auf der Clientseite) und das Skeleton (der Proxy auf der Serverseite) automatisch zur Laufzeit aufgebaut. Das Skeleton richtet die Clientanfrage an die wirkliche Methodenimplementierung und schickt das Ergebnis wieder zurück.

Dieser Automatismus funktioniert nur dann automatisch, wenn auf beiden Seiten eine Java-5-Implementierung vorhanden ist. Ist dies nicht der Fall, muss rmic doch noch zum Einsatz kommen.


Galileo Computing

18.3.4 Der Namensdienst (Registry)  downtop

Durch den Namensdienst kann ein Server entfernte Objekte mit einem Namen anmelden, und Clients können die entfernten Objekte unter einem Namen finden. Für den Namensdienst sind unterschiedliche Programme möglich; beim Java-JDK ist ein einfaches Programm dabei.

Unter Windows starten wir den Dienst in einer neuen DOS-Box (sozusagen im Hintergrund) mit folgender Zeile:

$ start rmiregistry

Unter Unix-Systemen so:

$ rmiregistry &

Die Registry können wir uns somit als einen einfachen Assoziativspeicher vorstellen, der Namen und Stub-Objekte verbindet. Der Zustand des Stubs wird bei der Registry hinterlegt.

Der Port

Der Namensdienst läuft standardmäßig auf dem Port 1099. Für Dienste hinter einer Firewall ist es bedeutend, dass dieser Port auch anders lauten kann. Eine andere Port-Nummer lässt sich einfach als Argument angeben:

$ start rmiregistry 2001

Der angegebene Port dient nur der Vermittlung vom Client zum Namensdienst. Die Kommunikation von Client und Server läuft über einen anderen Port.

An dieser Stelle haben wir schon fast alles zusammen. Der Namensdienst läuft und wartet auf den Server und den Client. Beginnen wir mit dem Server. Er ist ein normales Java-Programm ohne Einschränkungen und muss weder etwas mit Remote noch mit Serializable zu schaffen haben.


Galileo Computing

18.3.5 Remote-Objekt-Implementierung exportieren und beim Namensdienst anmelden  downtop

Bevor ein Client sich mit dem Server verbinden und Methoden ausführen kann, muss die Remote-Objekt-Implementierung exportiert werden. Hierfür bietet Java die Klasse UnicastRemoteObject, die wir auf zwei Arten nutzen können:

  • Unsere Remote-Objekt-Implementierung erweitert die Klasse UnicastRemoteObject und exportiert sich über den Konstruktor selbst.
  • Wir exportieren – wie in unserem Fall – die Implementierung mit UnicastRemoteObject.exportObject().

Im nächsten Schritt muss das Remote-Objekt dem Namensdienst bekannt gemacht werden. Dazu dient die Methode rebind() oder bind().

Listing 18.3    com/javatutor/insel/rmi/Server.java

package com.javatutor.insel.rmi; 
 
import java.rmi.registry.LocateRegistry; 
import java.rmi.registry.Registry; 
import java.rmi.server.RemoteServer; 
import java.rmi.server.UnicastRemoteObject; 
 
public class Server 
{ 
  public static void main( String[] args ) throws Exception 
  { 
    AdderImpl adder = new AdderImpl(); 
    Adder stub = (Adder) UnicastRemoteObject.exportObject( adder, 0 ); 
    RemoteServer.setLog( System.out ); 
 
    Registry registry = LocateRegistry.getRegistry(); 
    registry.rebind( "Adder", stub ); 
 
    System.out.println( "Adder angemeldet" ); 
  } 
}

Anmelden am Namensdienst

An diesem Programm ist abzulesen, dass das Eintragen sehr einfach ist. Es ist als assoziative Datenstruktur zu verstehen, die einen Objektnamen mit einem entfernten Objekt assoziiert. Die Notation beim Anmelden für das Objekt ist wie bei einer URL:

rmi://Host:Port/Objektname

Wenn ein alternativer Port für den Namensdienst gewählt wurde, stellen wir diesen mit einem Doppelpunkt wie üblich hinten an – sonst läuft der Namensdienst standardmäßig unter 1099. Der vorangestellte Protokollname rmi ist optional, sodass er auch weggelassen werden kann. Ist kein Rechnername angegeben, wird localhost angenommen. Daher steht im oberen Beispiel einfach nur rebind("Adder", stub).

Zum Binden der Informationen bietet der Namensdienst zwei unterschiedliche Funktionen an:

  • bind() trägt den Dienst im Namensdienst ein, aber wenn schon ein anderer Dienst unter dem gleichen Namen läuft, wird eine AlreadyBoundException ausgelöst.
  • rebind() dagegen fügt einen neuen Eintrag mit dem gleichen Namen hinzu oder überschreibt den alten.

Und abmelden

Ist der Dienst nicht mehr erwünscht, meldet unbind() ihn wieder ab. Der Namensdienst muss wie beim Anmelden laufen. Aus Sicherheitsgründen erlaubt der Namensdienst es nur dem Server, der das Objekt seinerzeit angemeldet hat, es wieder abzumelden. Einen zusätzlichen Namen müssen wir nicht angeben.


final class java.rmi.registry.LocateRegistry

  • static void static Registry getRegistry() Liefert einen Verweis auf die Registry oder löst eine RemoteException aus, wenn die Registry nicht lokalisiert werden konnte.

interface java.rmi.registry.Registry 
extends Remote

  • void bind( String name, Remote obj ) throws AlreadyBoundException, MalformedURLException, RemoteException Bindet das Objekt ref, das in der Regel der Stub ist, an den Namen name und trägt es so in der Registrierung ein. Eine AlreadyBoundException zeigt an, dass der Name schon vergeben ist. Die MalformedURLException informiert, wenn der Name ungültig gebunden ist. Eine RemoteException wird ausgelöst, wenn der Namensdienst nicht erreicht werden konnte. Fehlende Rechte führen zu einer AccessException.
  • void rebind( String name, Remote obj ) Verhält sich wie bind(), mit dem Unterschied, dass Objekte ersetzt werden, sofern sie schon angemeldet sind.
  • void unbind( String name ) Entfernt das Objekt aus der Registrierung. Ist das Objekt nicht gebunden, folgt eine NotBoundException. Die anderen Fehler sind wie bei bind().

Galileo Computing

18.3.6 Einfaches Logging  downtop

Um die Aktivität von RMI verfolgen zu können, haben die Entwickler einen einfachen Login-Mechanismus eingebaut. Er gibt Auskunft über die Objekte und entfernte Referenzen. Hier erfahren wir auch, ob alle gewünschten Objekte korrekt gefunden wurden. Das Logging lässt sich mit der Eigenschaft java.rmi.server.logClass einschalten, wenn der Wert auf true gesetzt ist. Dann erscheinen Ausgaben auf dem System.err-Fehlerkanal. Außerdem setzt die statische Funktion RemoteServer.setLog(OutputStream) einen Fehlerausgabestrom. Die Methode getLog() liefert diesen Fehlerkanal allerdings als aufgewerteten PrintStream.


abstract class java.rmi.server.RemoteServer 
extends RemoteObject

  • static void setLog( OutputStream out ) Loggt RMI-Aufrufe, indem sie in den Ausgabestrom out geschrieben werden. Ist out=null, wird das Logging beendet.
  • static PrintStream getLog() Liefert den Ausgabestrom für das RMI-Logging.

Tipp Das Paket java.rmi.server hält noch eine andere Klasse bereit, die recht nützlich sein kann: UID. Mit dieser Klasse lässt sich eine einfache ID berechnen.
String s = new java.rmi.server.UID().toString();


Galileo Computing

18.3.7 Aufräumen mit dem DGC  toptop

Im Fall von verteilten Anwendungen reicht der normale GC nicht, und das Konzept muss um einen verteilten GC (engl. distributed GC, kurz DGC) erweitert werden. Im lokalen Fall weiß die lokale Maschine immer, ob ein Objekt referenziert wird, bei verteilten Anwendungen kann aber auf dem Server ein Objekt existieren, für das sich kein Mensch mehr interessiert. Damit bei verteiltenAnwendungen auch der GC nicht mehr benutzte Objekte auf der Serverseite wegräumen kann, verschickt die Maschine beim Nutzen und Lösen von Verbindungen referenced- beziehungsweise dereferenced-Meldungen. Ist die Verbindung dann gelöst, bleibt die Klasse jedoch noch einige Zeit auf dem Server und wird nicht sofort gelöst. Aussagen über die Verweildauer gibt die Lease an, die sich über eine Eigenschaft verändern lässt.


Beispiel Setze die Verweildauer für Objekte auf eine halbe Stunde hoch:
$ java -Djava.rmi.dgc.leaseValue=30 MyClass

Die Standarddauer ist auf 10 Minuten gesetzt.




Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.





 <<   zurück



Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de